package org.xwalk.core;

import android.util.Log;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * This class represents the preferences and could be set by callers.
 * It is not thread-safe and must be called on the UI thread.
 * Afterwards, the preference could be read from all threads and can impact
 * all XWalkView instances.
 */

public class XWalkPreferences   {

    /**
     * The key string to enable/disable remote debugging.
     * @since 1.0
     */
    public final static String REMOTE_DEBUGGING = "remote-debugging";

    /**
     * The key string to enable/disable animatable XWalkView. Default value is
     * false.
     *
     * If this key is set to false, then SurfaceView will be created internally as the
     * rendering backend.
     * If this key is set to true, the XWalkView created by Crosswalk can be
     * transformed and animated. Internally, Crosswalk is alternatively using
     * TextureView as the backend of XWalkView.
     *
     * <a href="http://developer.android.com/reference/android/view/TextureView.html">
     * TextureView</a> is a kind of
     * <a href="http://developer.android.com/reference/android/view/View.html">
     * android.view.View</a> that is different from
     * <a href="http://developer.android.com/reference/android/view/SurfaceView.html">
     * SurfaceView</a>. Unlike SurfaceView, it can be resized, transformed and
     * animated. Once this key is set to true, all XWalkView will use TextureView
     * as the rendering target instead of SurfaceView. The downside of TextureView
     * is, it would consume more graphics memory than SurfaceView and may have
     * 1~3 extra frames of latency to display updates.
     *
     * Note this key MUST be set before creating the first XWalkView, otherwise
     * a RuntimeException will be thrown.
     *
     * @since 2.0
     */
    public final static String ANIMATABLE_XWALK_VIEW = "animatable-xwalk-view";

    /**
     * The key string to allow/disallow javascript to open
     * window automatically.
     * @since 3.0
     */
    public final static String JAVASCRIPT_CAN_OPEN_WINDOW = "javascript-can-open-window";

    /**
     * The key string to allow/disallow having universal access
     * from file origin.
     * @since 3.0
     */
    public final static String ALLOW_UNIVERSAL_ACCESS_FROM_FILE = "allow-universal-access-from-file";

    /**
     * The key string to enable/disable multiple windows.
     * @since 3.0
     */
    public final static String SUPPORT_MULTIPLE_WINDOWS = "support-multiple-windows";

    /**
     * The key string to set xwalk profile name.
     * User data will be kept separated for different profiles.
     * Profile needs to be set before any XWalkView instance created.
     * @since 3.0
     */
    public final static String PROFILE_NAME = "profile-name";

    /**
     * The key string to enable/disable spatial navigation like a TV controller.
     * @since 6.0
     */
    public final static String SPATIAL_NAVIGATION = "enable-spatial-navigation";


    public final static String ENABLE_THEME_COLOR = "enable-theme-color";

    /**
     * The key string to enable/disable javascript.
     * @since 7.0
     */
    public final static String ENABLE_JAVASCRIPT = "enable-javascript";

    /**
     * The key string to enable/disable xwalk extensions.
     * @since 7.0
     */
    public final static String ENABLE_EXTENSIONS = "enable-extensions";



    private static XWalkCoreWrapper coreWrapper;


    /**
     * Set a boolean preference value into Crosswalk. An exception will be thrown if
     * the key for the preference is not valid.
     * @param key the string name of the key.
     * @param enabled true if setting it as enabled.
     * @since 1.0
     */
    public static void setValue(String key, boolean enabled) {
        reflectionInit();
        try {
            setValueStringbooleanMethod.invoke(key, enabled);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                setValueStringbooleanMethod.setArguments(key, enabled);
                XWalkCoreWrapper.reserveReflectMethod(setValueStringbooleanMethod);
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        
    }

    private static ReflectMethod setValueStringbooleanMethod = new ReflectMethod(null, "setValue");

    /**
     * Set an integer preference value into Crosswalk. An exception will be thrown if
     * the key for the preference is not valid.
     * @param key the string name of the key.
     * @param value the integer value.
     * @since 3.0
     */
    public static void setValue(String key, int value) {
        reflectionInit();
        try {
            setValueStringintMethod.invoke(key, value);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                setValueStringintMethod.setArguments(key, value);
                XWalkCoreWrapper.reserveReflectMethod(setValueStringintMethod);
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        
    }

    private static ReflectMethod setValueStringintMethod = new ReflectMethod(null, "setValue");

    /**
     * Set a string preference value into Crosswalk. An exception will be thrown if
     * the key for the preference is not valid.
     * @param key the string name of the key.
     * @param value the string value.
     * @since 3.0
     */
    public static void setValue(String key, String value) {
        reflectionInit();
        try {
            setValueStringStringMethod.invoke(key, value);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                setValueStringStringMethod.setArguments(key, value);
                XWalkCoreWrapper.reserveReflectMethod(setValueStringStringMethod);
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        
    }

    private static ReflectMethod setValueStringStringMethod = new ReflectMethod(null, "setValue");

    /**
     * Get a boolean preference value from Crosswalk. An exception will be thrown if
     * the key for the preference is not valid.
     * @param key the string name of the key.
     * @return true if it's enabled.
     * @since 1.0
     * @deprecated
     */
    public static boolean getValue(String key) {
        reflectionInit();
        try {
            return (Boolean) getValueStringMethod.invoke(key);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return false;
    }

    private static ReflectMethod getValueStringMethod = new ReflectMethod(null, "getValue");

    /**
     * Get a boolean preference value from Crosswalk. An exception will be thrown if
     * the key for the preference is not valid.
     * @param key the string name of the key.
     * @return true if it's enabled.
     * @since 3.0
     */
    public static boolean getBooleanValue(String key) {
        reflectionInit();
        try {
            return (Boolean) getBooleanValueStringMethod.invoke(key);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return false;
    }

    private static ReflectMethod getBooleanValueStringMethod = new ReflectMethod(null, "getBooleanValue");

    /**
     * Get a int preference value from Crosswalk. An exception will be thrown if
     * the key for the preference is not valid.
     * @param key the string name of the key.
     * @return the integer value.
     * @since 3.0
     */
    public static int getIntegerValue(String key) {
        reflectionInit();
        try {
            return (Integer) getIntegerValueStringMethod.invoke(key);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return 0;
    }

    private static ReflectMethod getIntegerValueStringMethod = new ReflectMethod(null, "getIntegerValue");

    /**
     * Get a string preference value from Crosswalk. An exception will be thrown if
     * the key for the preference is not valid.
     * @param key the string name of the key.
     * @return the string value.
     * @since 3.0
     */
    public static String getStringValue(String key) {
        reflectionInit();
        try {
            return (String) getStringValueStringMethod.invoke(key);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return null;
    }

    private static ReflectMethod getStringValueStringMethod = new ReflectMethod(null, "getStringValue");


    static void reflectionInit() {
        if (coreWrapper != null) return;

        XWalkCoreWrapper.initEmbeddedMode();

        coreWrapper = XWalkCoreWrapper.getInstance();
        if (coreWrapper == null) {
            XWalkCoreWrapper.reserveReflectClass(XWalkPreferences.class);
            return;
        }

        Class<?> bridgeClass = coreWrapper.getBridgeClass("XWalkPreferencesBridge");

        setValueStringbooleanMethod.init(null, bridgeClass, "setValue", String.class, boolean.class);
        setValueStringintMethod.init(null, bridgeClass, "setValue", String.class, int.class);
        setValueStringStringMethod.init(null, bridgeClass, "setValue", String.class, String.class);
        getValueStringMethod.init(null, bridgeClass, "getValue", String.class);
        getBooleanValueStringMethod.init(null, bridgeClass, "getBooleanValue", String.class);
        getIntegerValueStringMethod.init(null, bridgeClass, "getIntegerValue", String.class);
        getStringValueStringMethod.init(null, bridgeClass, "getStringValue", String.class);
    }
  
}
